import java.util.*;
import java.util.regex.*;

public class NewConverter {

	int variableIndex;
	int variableAddIndex;
	XSLconverter xslConverter;
	String resultXSL;
	String resultVariables;

	public NewConverter() {
		variableIndex = 0;
		variableAddIndex = 0;
		xslConverter = new XSLconverter();
		resultXSL = "";
		resultVariables = "";
	}
	
	/**
	 * used to put on the final touches to the converted XSL
	 * 
	 * example: 
	 * 	convertWithFinalComponents("@AP:1")
	 * 
	 * returns:
	 * 	<?xml version="1.0" encoding="UTF-8"?>
	 * 
	 * 	<xsl:stylesheet version="2.0"xmlns:fn="http://www.w3.org/2005/xpath-functions" 
	 * 	xmlns:mismo="http://www.mismo.org/residential/2009/schemas" 
	 * 	xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	 * 		<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
	 * 			<!-- Functions -->
	 * 			<xsl:template match="/">
	 * 				<AllMappings>
	 * 					<xsl:apply-templates select="ApVariables"/>
	 * 				</AllMappings>
	 * 			</xsl:template>
	 * 		<!-- Match ApVariables Template -->
	 * 		<xsl:template match="ApVariables">
	 * 			<Mapping>
	 * 				<xsl:value-of select="AP_1"/>
	 * 			</Mapping>
	 * 		</xsl:template>
	 * 	</xsl:stylesheet>
	 *
	 * @param defStringToConvert string containing DEF programming code
	 * @return string containing XSL headers, built-in functions, and converted code
	 */
	public String convertWithFinalComponents(String defStringToConvert) { //used to put on the final touches to the converted xsl
		
		this.convert(defStringToConvert);
		return new getFinalXSLstring(resultVariables, resultXSL, UsedFunctions.getUsedFunctionsAsString()).finalXSL;

	}
	
	public void convert(String defStringToConvert) {

		int currentCharacterPositionForSearchingRegexMatches = 0;
		List<MatcherMethod> matcherMethodList = new MatchersForDEFCommandsAndLiterals(defStringToConvert).list;

		boolean noMatchesWereFound = false;

		//look for matches for each character position 
		while (currentCharacterPositionForSearchingRegexMatches < defStringToConvert.length() && noMatchesWereFound == false) {

			boolean matchingPatternAlreadyFound = false;
			
			//look for matches by checking each pattern in the list of valid patterns
			for (MatcherMethod currentMatcherMethod : matcherMethodList) {
				if (matchingPatternAlreadyFound == false) {
					if (matchExistsAtThisPosition(currentMatcherMethod.matcher, currentCharacterPositionForSearchingRegexMatches)) {
						matchingPatternAlreadyFound = true; //for when we support constants, which would almost always match; also allows for updated currentCharPosition
						//if function match
						if (isCurrentMatcherMethodFunction(currentMatcherMethod.XSLMethod)) {
							//System.out.print(" - match found that is a function: " + currentMatcherMethod.matcher.group());
							//System.out.println(" - length of match: " + currentMatcherMethod.matcher.group().length());
							String entireFunctionStringToTheLastParentheses = getFunctionStringByIndexingParentheses(defStringToConvert.substring(currentCharacterPositionForSearchingRegexMatches));
							UsedFunctions.addFunctionToUsedFunctions(currentMatcherMethod.XSLMethod);
							resultXSL += DEFfunctionConverters.getResultFromInvokedMethod(currentMatcherMethod.XSLMethod, entireFunctionStringToTheLastParentheses);
							resultVariables += DEFfunctionConverters.getResultFromInvokedMethod(currentMatcherMethod.variableMethod, entireFunctionStringToTheLastParentheses);
							currentCharacterPositionForSearchingRegexMatches += entireFunctionStringToTheLastParentheses.length();
							//currentCharacterPositionForSearchingRegexMatches += currentMatcherMethod.matcher.end();
						} else if (isCurrentMatcherMethodIfOrLoop(currentMatcherMethod.XSLMethod)) { //if it is an 'if' match
							
							resultXSL += DEFfunctionConverters.getResultFromInvokedMethod(currentMatcherMethod.XSLMethod, defStringToConvert.substring(currentCharacterPositionForSearchingRegexMatches));
							resultVariables += DEFfunctionConverters.getResultFromInvokedMethod(currentMatcherMethod.variableMethod, defStringToConvert.substring(currentCharacterPositionForSearchingRegexMatches));
							currentCharacterPositionForSearchingRegexMatches += getLengthOfIfOrLoop(defStringToConvert.substring(currentCharacterPositionForSearchingRegexMatches));
						
						} else //if non-function and non-if match
						{
							//currentCharacterPositionForSearchingRegexMatches++;
							
							//System.out.print(" - match found that is not a function: " + currentMatcherMethod.matcher.group());
							//System.out.println(" - length of match: " + currentMatcherMethod.matcher.group().length());
							resultXSL += DEFfunctionConverters.getResultFromInvokedMethod(currentMatcherMethod.XSLMethod, defStringToConvert.substring(currentCharacterPositionForSearchingRegexMatches));
							resultVariables += DEFfunctionConverters.getResultFromInvokedMethod(currentMatcherMethod.variableMethod, defStringToConvert.substring(currentCharacterPositionForSearchingRegexMatches));
							currentCharacterPositionForSearchingRegexMatches += currentMatcherMethod.matcher.group().length();
						}
					} else {
						//System.out.println(" - match not found");
					}
				} else {
					
				}
			}
			
			//no matches found at this character position after testing every pattern in the list of valid patterns
			if (matchingPatternAlreadyFound == false) {
				noMatchesWereFound = true;
			}
		}
	}
	
	public static String getFunctionStringByIndexingParentheses(String string) {
	
	//System.out.print("returning entire function string for: " + string);
	
	int characterIndexOfFirstParentheses= 0;
	while(string.charAt(characterIndexOfFirstParentheses) != '(') characterIndexOfFirstParentheses++;
	
	//System.out.println("opening parentheses found at character position: " + characterIndexOfFirstParentheses);
	
	int currentCountOfHangingParentheses = 1;
	String functionString = string.substring(0, characterIndexOfFirstParentheses + 1);
	int currentCharacterIndexToMatchParentheses = characterIndexOfFirstParentheses + 1;
	while (currentCountOfHangingParentheses > 0)
	{
		if (string.charAt(currentCharacterIndexToMatchParentheses) == '(') {
			currentCountOfHangingParentheses++;
		}
		else if (string.charAt(currentCharacterIndexToMatchParentheses) == ')') {
			currentCountOfHangingParentheses--;
		}
		functionString = functionString.concat(String.valueOf(string.charAt(currentCharacterIndexToMatchParentheses)));
		currentCharacterIndexToMatchParentheses++;
	}
	//System.out.println(" - return: " + functionString);
	return functionString;
	}

	private int getLengthOfIfOrLoop(String string) {
		
		int i=0;
		for (; i<string.length() && string.charAt(i) != ']'; i++) {}
		return i;
		
	}
	
	private boolean isCurrentMatcherMethodFunction(String method) {
	
	List<String> functionMethodNamesList = new FunctionMethodNames().list;
	boolean tempBoolValue = false;
	for (String s:functionMethodNamesList) {
			if (method == s) {
				tempBoolValue = true;
				break;
			}
			else {
				tempBoolValue = false;	
			}
		}
	return tempBoolValue;
	}

	private boolean isCurrentMatcherMethodIfOrLoop(String method) {
		
		if (method == "convertIfToXSL" || method == "convertLoopToXSL") {
			return true;
		}
		return false;
	}

	private boolean matchExistsAtThisPosition(Matcher matcher, int currentCharacterPositionForSearchingRegexMatches) {
		//System.out.print("Searching for match: " + matcher.pattern().toString() + " at " + currentCharacterPositionForSearchingRegexMatches);
		return matcher.find(currentCharacterPositionForSearchingRegexMatches) && currentCharacterPositionForSearchingRegexMatches == matcher.start();
	}

}
